#include <ctime>

#include <qapplication.h>
#include "window.moc"
#include "glwidget.h"
#include "ucdinputfile.h"
#include "visTopoClustering.h"
#include "visKDTree.h"
kdTree *tree;

//#define SOURCES_POINT
//#define STREAMLINES
#define CLUSTERING_RESULT

using namespace visualization;
using namespace std;

size_t centerNum, iterations;
class glW: public GLWidget
{
public:
	glW(QWidget *parent = 0) : GLWidget(parent), 
														 fs(NULL), bs(NULL),
														 forward(NULL), backward(NULL),
														 sourcesInDomains(NULL), clusterCenters(NULL)
	{} 
	~glW()
	{
		delete forward;;
		delete backward;

		delete []fs;
		delete []bs;
		delete []sourcesInDomains;
		for(size_t cnt = 0; cnt < saddle.size() * QUADRANTS; cnt++)
		{
			delete []clusterCenters[cnt].center;
		}
		delete []clusterCenters;
	}
	void set(vector<saddlePoint> &sdl, intgResults *fwd, intgResults *bwd,
					 Point *fwdSources, Point *bwdSources, SourcesAroundSaddle *srcInDomain,
					 double bds[6], vector< vector<Point> > &strmLines, ClusterCenters *clusterCnt,
					 size_t cnts)
	{
		saddle = sdl;
		forward = fwd;
		backward = bwd;
		fs = fwdSources;
		bs = bwdSources;
		sourcesInDomains = srcInDomain;
		for(size_t i = 0; i < 6; i++) { boundaries[i] = bds[i]; }
		streamlines = strmLines;
		clusterCenters = clusterCnt;
		cntNum = cnts;
	}
	GLuint makeObject();

private:
	Point *fs, *bs;
	intgResults *forward, *backward;
	vector<saddlePoint> saddle;
	double boundaries[6];
	SourcesAroundSaddle *sourcesInDomains;
	vector< vector<Point> > streamlines;
	ClusterCenters *clusterCenters;
	size_t cntNum;
};

class win : public Window
{
public:
	win(glW *glwidget)
		: Window(glwidget)
	{}
	~win(){}
};

void compute(string file, string param, vector<saddlePoint> &saddle, 
						 intgResults *&fwd, intgResults *&bwd, 
						 Point *&fwdSources, Point *&bwdSources, 
						 SourcesAroundSaddle *&srcInDomain,
						 double boundaries[6], vector< vector<Point> > &strmlines,
						 ClusterCenters *&clusterCnt, size_t &cnts);

int main(int argc, char *argv[])
{
	if(argc < 2)
	{
		cerr << endl << "Both a data file and a parameter file are needed..." << endl << endl;
		return 0;
	}

	vector<saddlePoint> saddle;
	intgResults *fwd, *bwd;
	Point *fwdSources, *bwdSources;
	SourcesAroundSaddle *srcInDomain;
	double boundaries[6];
	vector< vector<Point> > strmLines;
	ClusterCenters *clusterCnt;
	size_t cnts;
	compute(argv[1], argv[2], saddle, fwd, bwd, fwdSources, 
					bwdSources, srcInDomain, boundaries, 
					strmLines, clusterCnt, cnts);

  QApplication app(argc, argv);
	glW *glw = new glW();
	glw->set(saddle, fwd, bwd, fwdSources, bwdSources, srcInDomain, boundaries, strmLines, clusterCnt, cnts);
  win window(glw);
	window.generateWindow();
  window.show();
  app.exec();
	delete tree;
	return 0;
}

void compute(string file, string param, vector<saddlePoint> &saddle, 
						 intgResults *&fwd, intgResults *&bwd, 
						 Point *&fwdSources, Point *&bwdSources, 
						 SourcesAroundSaddle *&srcInDomain,
						 double boundaries[6], vector< vector<Point> > &strmLines,
						 ClusterCenters *&clusterCnt, size_t &cnts)
{
	visTopoClustering TC;
	TC.setFileName(file);
	TC.parseParameter(param);
	TC.clusterDomain();

#ifdef CLUSTERING_RESULT
	time_t begin, end;
	begin = clock();
	if(1 == TC.m_UseThreshold) TC.postProcess();
	if(1 == TC.m_UseRemoveDuplicate) TC.removeDuplicate();
	end = clock();
	cout << endl << "TIME USED FOR POSTPROCESSING	:	" << (double)(end - begin)/CLOCKS_PER_SEC << endl;
#endif

	TC.getTopoStructure(fwd, bwd, saddle);
	TC.getSources(fwdSources, bwdSources);
	srcInDomain = TC.getSourcesInDomain();
	TC.getBoundaries(boundaries);

	//tree = TC.intgCurvesAndBoundaries;
	strmLines = TC.m_Streamlines;
	clusterCnt = TC.getClusterCenters();
	cnts = TC.getCenterNumber();
	centerNum = TC.m_CenterNum;
	iterations = TC.m_Iterations;
}
GLuint glW::makeObject()
{
	GLuint list = glGenLists(1);
	glNewList(list, GL_COMPILE);

	glEnable( GL_POINT_SMOOTH );
	glEnable( GL_LINE_SMOOTH );
	glEnable( GL_BLEND );
	double c[3] = 
	{
		(boundaries[0]+boundaries[1])/2,
		(boundaries[2]+boundaries[3])/2,
		(boundaries[4]+boundaries[5])/2
	};
	double scale = 100;
	glBegin(GL_LINES);
	drawFramework(boundaries[0]-c[0], boundaries[1]-c[0],
								boundaries[2]-c[1], boundaries[3]-c[1],
								boundaries[4]-c[2], boundaries[5]-c[2], scale);
	glEnd();

	glLineWidth(1);
	glPointSize(3);

#ifdef CLUSTERING_RESULT
	glColor3f(0, 0, 0);
	for(size_t quds = 0; quds < saddle.size() * QUADRANTS; quds++)
	{
		for(size_t cnt = 0; cnt < clusterCenters[quds].cntNum; cnt++)
		{
			if(NULL == clusterCenters[quds].center)continue;
			glBegin(GL_LINE_STRIP);
			for(size_t sz = 0; sz < clusterCenters[quds].center[cnt].size(); sz++)
			{
				glVertex3f( (clusterCenters[quds].center[cnt][sz].getX()-c[0])/scale,
									  (clusterCenters[quds].center[cnt][sz].getY()-c[1])/scale,
									 	0
									);
			}
			glEnd();
		}
	}
#endif

#ifdef SOURCES_POINT
	glColor3f(0, 0, 0);
	for(size_t sdl = 0; sdl < saddle.size(); sdl++)
	{
		for(size_t dmn = 0; dmn < QUADRANTS; dmn++)
		{
			glBegin(GL_POINTS);
			for(size_t k = 0; k < sourcesInDomains[sdl].sources[dmn].size(); k++)
			{
				Point tmp = sourcesInDomains[sdl].sources[dmn][k];
				glVertex3f( (tmp.getX()-c[0])/scale, (tmp.getY()-c[1])/scale, (tmp.getZ()-c[2])/scale);
			}
			glEnd();
		}
	}
#endif

#ifdef STREAMLINES
	glColor3f(0, 0, 0);
	for(size_t line = 0; line < streamlines.size(); line++)
	{
		glBegin(GL_LINE_STRIP);
		for(size_t pnt = 0; pnt < streamlines[line].size(); pnt++)
		{
			Point tmp = streamlines[line][pnt];
			glVertex3f( (tmp.getX()-c[0])/scale, (tmp.getY()-c[1])/scale, (tmp.getZ()-c[2])/scale);
		}
		glEnd();
	}
#endif

// //	Topology results.
	glLineWidth(2.5);
	glPointSize(5);
	glColor3f(0.0, 0.0, 0.0);
	glBegin(GL_POINTS);		
		for(size_t i = 0; i < saddle.size(); i++)
		{
			glVertex3f( (saddle[i].x-c[0])/scale, (saddle[i].y-c[1])/scale, 0);
		}
	glEnd();

	glColor3f(0, 0.0, 0.0);
	for(size_t i = 0; i < forward->size(); i++)
	{
		glBegin(GL_LINE_STRIP);
			for(size_t j = 0; j < (*forward)[i].size(); j++)
				glVertex3f(((*forward)[i][j].getX()-c[0])/scale, ((*forward)[i][j].getY()-c[1])/scale, 0);
		glEnd();
	}
	for(size_t i = 0; i < backward->size(); i++)
	{
		glBegin(GL_LINE_STRIP);
			for(size_t j = 0; j < (*backward)[i].size(); j++)
				glVertex3f(((*backward)[i][j].getX()-c[0])/scale, ((*backward)[i][j].getY()-c[1])/scale, 0);
		glEnd();
	}

	for(size_t i = 0; i < saddle.size(); i++)
	{
		glBegin(GL_LINE_STRIP);
			for(size_t j = 0; j < 2; j++)	
			{	
				glVertex3f((fs[2*i+j].getX()-c[0])/scale, (fs[2*i+j].getY()-c[1])/scale, 0);
			}
		glEnd();	
		glBegin(GL_LINE_STRIP);
			for(size_t j = 0; j < 2; j++) 
			{	
				glVertex3f((bs[2*i+j].getX()-c[0])/scale, (bs[2*i+j].getY()-c[1])/scale, 0);
			}
		glEnd();
	}

	glEndList();
	return list;
}

// Streamlines to cluster.

//	glColor3f(0, 1, 0);
//	for(size_t line = 0; line < streamlines.size(); line++)
//	{
//		glBegin(GL_LINE_STRIP);
//		for(size_t pnt = 0; pnt < streamlines[line].size(); pnt++)
//		{
//			Point tmp = streamlines[line][pnt];
//			glVertex3f( (tmp.getX()-c[0])/scale, (tmp.getY()-c[1])/scale, (tmp.getZ()-c[2])/scale);
//		}
//		glEnd();
//	}
//
//
//	glLineWidth(1.5);
//	// Clustering results.
//	glColor3f(0.1, 1, 1);
//	for(size_t quds = 0; quds < saddle.size() * QUADRANTS; quds++)
//	{
//		for(size_t cnt = 0; cnt < clusterCenters[quds].cntNum; cnt++)
//		{
//			if(NULL == clusterCenters[quds].center)continue;
//			glBegin(GL_LINE_STRIP);
//			for(size_t sz = 0; sz < clusterCenters[quds].center[cnt+clusterCenters[quds].cntNum*iterations].size(); sz++)
//			{
//				glVertex3f( (clusterCenters[quds].center[cnt+clusterCenters[quds].cntNum*iterations][sz].getX()-c[0])/scale,
//									  (clusterCenters[quds].center[cnt+clusterCenters[quds].cntNum*iterations][sz].getY()-c[1])/scale,
//									 	0
//									 );
//			}
//			glEnd();
//		}
//	}
//
//
//
	//glPointSize(2);
	//glColor3f(0, 0, 0);
	//cout << tree->maxNumOfNodes << endl;
	//glBegin(GL_POINTS);
	//for(size_t i = 0; i < tree->maxNumOfNodes; i++)
	//{
	//	glVertex3f( (tree->kdNodes[i].pos[0]-c[0])/scale,
	//						  (tree->kdNodes[i].pos[1]-c[1])/scale,
	//							(tree->kdNodes[i].pos[2]-c[2])/scale
	//						);
	//}
	//glEnd();

//	size_t count = 0;
//	glColor3f(1, 0, 0);
//	size_t cc = 0;
//
//	for(size_t quds = 0; quds < saddle.size() * QUADRANTS; quds++)
//	{
//		for(size_t cnt = 0; cnt < clusterCenters[quds].cntNum; cnt++)
//		{
//			if(cntToView != cc++) continue;
//			if(NULL == clusterCenters[quds].center)continue;
//			glBegin(GL_LINE_STRIP);
//				glVertex3f(
//										(streamlineArrows[count].getX() - c[0])/scale,
//										(streamlineArrows[count].getY() - c[1])/scale,
//										(streamlineArrows[count].getZ() - c[2])/scale
//									);
//				glVertex3f(
//										(streamlineArrows[count + 1].getX() - c[0])/scale,
//										(streamlineArrows[count + 1].getY() - c[1])/scale,
//										(streamlineArrows[count + 1].getZ() - c[2])/scale
//									);
//			glEnd();
//			glBegin(GL_LINE_STRIP);
//				glVertex3f(
//										(streamlineArrows[count].getX() - c[0])/scale,
//										(streamlineArrows[count].getY() - c[1])/scale,
//										(streamlineArrows[count].getZ() - c[2])/scale
//									);
//				glVertex3f(
//										(streamlineArrows[count + 2].getX() - c[0])/scale,
//										(streamlineArrows[count + 2].getY() - c[1])/scale,
//										(streamlineArrows[count + 2].getZ() - c[2])/scale
//									);
//			glEnd();
//			count += 3;
//		}
//	}
//
//	glPointSize(4);
//	glColor3f(1, 0, 0);
////	cout << "The saddle to view..." << endl;
////	size_t d;
////	cin >> d;
////	cout << "The quadrant to view .." << endl;
////	size_t ct;
////	cin >> ct;
//	for(size_t sdl = 0; sdl < saddle.size(); sdl++)
//	{
////		if(d != sdl)continue;
//		for(size_t dmn = 0; dmn < QUADRANTS; dmn++)
//		{
////			if(ct != dmn)continue;
////			cout << "gogogo	:	" << sourcesInDomains[sdl].sources[dmn].size() << endl;
//			glBegin(GL_POINTS);
//			for(size_t k = 0; k < sourcesInDomains[sdl].sources[dmn].size(); k++)
//			{
//				Point tmp = sourcesInDomains[sdl].sources[dmn][k];
//				glVertex3f( (tmp.getX()-c[0])/scale, (tmp.getY()-c[1])/scale, (tmp.getZ()-c[2])/scale);
//			}
//			glEnd();
//	
////			glBegin(GL_LINE_STRIP);
////			for(size_t k = 0; k < sourcesInDomains[sdl].sources[dmn].size(); k++)
////			{
////				Point tmp = sourcesInDomains[sdl].sources[dmn][k];
////				glVertex3f( (tmp.getX()-c[0])/scale, (tmp.getY()-c[1])/scale, (tmp.getZ()-c[2])/scale);
////			}
////			glEnd();
//		}
//	}




